package com.kuaidu.ll.quickreadnews.utils;

import android.util.Log;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.concurrent.ExecutorService;

/**
 * Created by liulei on 2017/7/14.
 */
public class KDLog {
    private static final String TAG_PREFIX = "KD|";

    private static final boolean ENABLE_VERBOSE = true;
    public static final boolean ENABLE_DEBUG = true;
    private static final boolean ENABLE_INFO = true;
    private static final boolean ENABLE_WARN = true;
    private static final boolean ENABLE_ERROR = true;
    private static final boolean ENABLE_LOG_META = true;
    private static final boolean ENABLE_LOG_TRACE_INFO = false;

    private static final int DEBUG_NONE = -1;
    private static final int DEBUG_INFO = 0;
    private static final int DEBUG_WARN = 1;
    private static final int DEBUG_ERROR = 2;
    /**
     * -1 不打印log 0 打印所有log 1 打印警告以上的log 2 打印错误的log
     */
    private static int DEBUG_LEVEL = DEBUG_INFO;
    /**
     * 是否写日志到文件
     */
    private static boolean DEBUG_FILE = true;
    public static boolean ENABLE_BLE_LOG = true;
    /**
     * 根据调用栈关系动态调整STACK_LEVEL的值；
     * 如 0 为最底层，那么打印的将是 Debug.java _FUNC_；
     * 如 1 为直接调用者的信息；
     */
    private static final int STACK_LEVEL = 2;
    private static boolean ENABLE_LOG = false;
    private static boolean ENABLE_TRACE = true;
    private static boolean ENABLE_ASSERT = true;
    private static boolean ENABLE_DEBUG_LOCK = true;

    public static boolean isLoggable(String tag) {
        return Log.isLoggable(tag, Log.VERBOSE);
    }

    public static void ASSERT_NOT_NULL(final Object obj) {
        ASSERT_NOT_NULL(obj,"");
    }
    public static void  ASSERT_NOT_NULL(final Object obj,String msg){
        if (ENABLE_ASSERT && obj == null) {
            LOG(TAG_PREFIX, ">>>"+msg+" `这个对象为空`这个对象为空 断言不成立<<<", 0, 'e');
        }
    }

    public static void ASSERT_NULL(final Object obj) {
        ASSERT_NULL(obj,"");
    }
    public static void ASSERT_NULL(final Object obj ,String msg){
        if (ENABLE_ASSERT && obj != null) {
            LOG(TAG_PREFIX, ">>>"+msg+" `这个对象不为空` 断言不成立<<<", 0, 'e');
        }
    }
    public static void ASSERT_RUN_ON_THREAD(final Thread thread) {
        if (ENABLE_ASSERT && thread != null && Thread.currentThread().getId() != thread.getId()) {
            LOG(TAG_PREFIX, ">>> `不运行在当前当前线程` 断言不成立 <<<", 0, 'e');
        }
    }

    public static void ASSERT_TRUE(final boolean cond) {
       ASSERT_TRUE(cond,"");
    }
    public static void ASSERT_TRUE(final boolean cond,String msg)
    {
        if (ENABLE_ASSERT && !cond) {
            LOG(TAG_PREFIX, ">>>FALSE"+msg+" `是` 断言不成立<<<", 0, 'e');
        }
    }

    public static void DEBUG(final String msg) {
        if (ENABLE_DEBUG) {
            LOG(TAG_PREFIX, msg, 0, 'd');
        }
    }

    public static void DEBUG_LOCK(final String msg) {
        if (ENABLE_DEBUG_LOCK) {
            LOG(TAG_PREFIX, "LOCK#" + msg, 0, 'v');
        }
    }

    public static void e(String tag, String message) {
        if (DEBUG_LEVEL > DEBUG_NONE) {
            Log.e(TAG_PREFIX + tag, _FUNC_() + message);
        }
    }

    public static void e(String tag, String message, Throwable e) {
        if (DEBUG_LEVEL > DEBUG_NONE) {
            Log.e(TAG_PREFIX + tag, _FUNC_() + message, e);
        }
    }

    public static void enable(boolean logcatEnable, boolean fileLogEnable) {
        DEBUG_LEVEL = logcatEnable ? DEBUG_INFO : DEBUG_NONE;
        enableM(logcatEnable);
        DEBUG_FILE = fileLogEnable;
    }

    public static void enableM(boolean enable) {
        ENABLE_LOG = enable;
        ENABLE_TRACE = enable;
        ENABLE_ASSERT = enable;
        ENABLE_DEBUG_LOCK = enable;
    }

    public static void ERROR(final String msg) {
        if (ENABLE_ERROR) {
            LOG(TAG_PREFIX, msg, 0, 'e');
        }
    }

    public static void BleDebug(final String msg) {
        if (!ENABLE_BLE_LOG)
            return;
        DEBUG(msg);
    }


    public static void i(String tag, String message) {
        if (DEBUG_LEVEL > DEBUG_NONE && DEBUG_LEVEL < DEBUG_WARN) {
            Log.i(TAG_PREFIX + tag, _FUNC_() + message);
        }
    }

    public static void INFO(final String msg) {
        if (ENABLE_INFO) {
            LOG(TAG_PREFIX, msg, 0, 'i');
        }
    }

    public static boolean isEnabled() {
        return DEBUG_LEVEL > DEBUG_NONE && DEBUG_LEVEL < DEBUG_ERROR;
    }

    public static boolean isEnabledFile() {
        return DEBUG_FILE;
    }

    public static void l(String tag, String message) {
        if (DEBUG_LEVEL > DEBUG_NONE && DEBUG_LEVEL < DEBUG_WARN) {
            int maxLogSize = 1000;
            for (int i = 0; i <= message.length() / maxLogSize; i++) {
                int start = i * maxLogSize;
                int end = (i + 1) * maxLogSize;
                end = end > message.length() ? message.length() : end;
                Log.i(TAG_PREFIX + tag, _FUNC_() + message.substring(start, end));
            }
        }
    }

    public static void line() {
        if (DEBUG_LEVEL > DEBUG_NONE && DEBUG_LEVEL < DEBUG_WARN) {
            StackTraceElement traceElement = new Exception().getStackTrace()[STACK_LEVEL - 1];

            String sb = traceElement.getMethodName() +
                    ":" +
                    traceElement.getLineNumber();

            String className = traceElement.getClassName();
            className = className.substring(className.lastIndexOf(".") + 1);

            Log.i(TAG_PREFIX + className, sb);
        }
    }

    public static void TRACE() {
        if (ENABLE_TRACE) {
            LOG(TAG_PREFIX, "<<<<====", 0, 'v');
        }
    }

    public static void VERBOSE(final String msg) {
        if (ENABLE_VERBOSE) {
            LOG(TAG_PREFIX, msg, 0, 'v');
        }
    }

    public static void w(String tag, String message) {
        if (DEBUG_LEVEL > DEBUG_NONE && DEBUG_LEVEL < DEBUG_ERROR) {
            Log.w(TAG_PREFIX + tag, _FUNC_() + message);
        }
    }

    public static void w(String tag, String msg, Throwable tr) {
        if (DEBUG_LEVEL > DEBUG_NONE && DEBUG_LEVEL < DEBUG_ERROR) {
            Log.w(TAG_PREFIX + tag, msg, tr);
        }
    }

    public static void d(String tag, String msg) {
        if (isLoggable(TAG_PREFIX + tag)) {
            Log.d(TAG_PREFIX + tag, msg);
        }
    }

    public static void WARN(final String msg) {
        if (ENABLE_WARN) {
            LOG(TAG_PREFIX, msg, 0, 'w');
        }
    }

    private static String _FUNC_() {
        StackTraceElement traceElement = new Exception().getStackTrace()[STACK_LEVEL];
        StringBuilder sb = new StringBuilder();
        sb.append("<");
        String className = traceElement.getClassName();
        className = className.substring(className.lastIndexOf(".") + 1);
        sb.append(className);
        sb.append(":");
        sb.append(traceElement.getMethodName());
        sb.append(":");
        sb.append(traceElement.getLineNumber());
        sb.append("> ");
        return sb.toString();
    }

    private static void LOG(final String TAG, final String msg, final int traceLevel, final char logLevel) {
        if (!ENABLE_LOG) {
            return;
        }

        final String threadName = Thread.currentThread().getName();
        final int DEFAULT_TRACE_LEVEL = 4;
        final StackTraceElement trace = Thread.currentThread().getStackTrace()[DEFAULT_TRACE_LEVEL + traceLevel];
        final String fullClassName = trace.getClassName();
        final String className = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
        final String methodName = trace.getMethodName();
        final int lineNo = trace.getLineNumber();
        final String meta = ENABLE_LOG_META ? "[" + threadName + "]" + "<" + className + ":" + methodName + ":"
                + lineNo + "> " : "";
        final String traceInfo = ENABLE_LOG_TRACE_INFO ? " >>> at " + trace : "";
        switch (logLevel) {
            case 'v':
                Log.v(TAG, meta + msg + traceInfo);
                break;
            case 'd':
                Log.d(TAG, meta + msg + traceInfo);
                break;
            case 'i':
                Log.i(TAG, meta + msg + traceInfo);
                break;
            case 'w':
                Log.w(TAG, meta + msg + traceInfo);
                break;
            case 'e':
                Log.e(TAG, meta + msg + traceInfo);
                break;
        }
    }

    private static void SHOW_LOGO() {
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
        INFO("MM.:  .:'   `:::  .:`MMMMMMMMMMM|`MMM'|MMMMMMMMMMM':  .:'   `:::  .:'.MM");
        INFO("MMMM.     :          `MMMMMMMMMM  :*'  MMMMMMMMMM'        :        .MMMM");
        INFO("MMMMM.    ::    .     `MMMMMMMM'  ::   `MMMMMMMM'   .     ::   .  .MMMMM");
        INFO("MMMMMM. :   :: ::'  :   :: ::'  :   :: ::'      :: ::'  :   :: ::.MMMMMM");
        INFO("MMMMMMM    ;::         ;::         ;::         ;::         ;::   MMMMMMM");
        INFO("MMMMMMM .:'   `:::  .:'   `:::  .:'   `:::  .:'   `:::  .:'   `::MMMMMMM");
        INFO("MMMMMM'     :           :           :           :           :    `MMMMMM");
        INFO("MMMMM'______::____      ::    .     ::    .     ::     ___._::____`MMMMM");
        INFO("MMMMMMMMMMMMMMMMMMM`---._ :: ::'  :   :: ::'  _.--::MMMMMMMMMMMMMMMMMMMM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMM::.         ::  .--MMMMMMMMMMMMMMMMMMMMMMMMMMM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMM-.     ;::-MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM. .:' .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM.   .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\\ /MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM .:~liulei~:. MM");
        INFO("MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM");
    }

   public enum Level{
        ERR, INFO, DBG
    }

    interface OutputConverter {
        String convert(String tag, String message);

        String convert(Throwable throwable, String tag, String message);

        String convert(Level level, Throwable throwable, String tag, String message);

        class DefaultOutputConverter implements OutputConverter {
            private static final String PATTERN = "%s %s/%s: %s";
            private static final String DATE_PATTERN = "yy-MM-dd HH:mm:ss";

            private final DateFormat mDateFormat = new SimpleDateFormat(DATE_PATTERN, Locale.US);

            @Override
            public String convert(Level level, Throwable throwable, String tag, String message) {
                String out = String.format(PATTERN, mDateFormat.format(new Date()), level.name(), tag, message);
                if (throwable != null) {
                    out += KDLog.convert(throwable);
                }
                return out;
            }

            @Override
            public String convert(Throwable throwable, String tag, String message) {
                return convert(Level.INFO, throwable, tag, message);
            }

            @Override
            public String convert(String tag, String message) {
                return convert(Level.INFO, null, tag, message);
            }
        }
    }

    public static String convert(Throwable t) {
        final StackTraceElement[] elements = t.getStackTrace();
        if (elements == null || elements.length == 0) {
            return t.toString();
        }

        final StringBuilder builder = new StringBuilder("\n").append(t.toString()).append('\n');

        // noinspection ForLoopReplaceableByForEach
        for (int i = 0, size = elements.length; i < size; i++) {
            builder.append("\tat ").append(elements[i].toString()).append('\n');
        }
        return builder.toString();
    }
}
